This is an R Markdown Notebook. When you execute code within the notebook, the results appear beneath the code.

Try executing this chunk by clicking the Run button within the chunk or by placing your cursor inside it and pressing Ctrl+Shift+Enter.

library(readxl)
library(lmtest) 
Loading required package: zoo

Attaching package: ‘zoo’

The following objects are masked from ‘package:base’:

    as.Date, as.Date.numeric
library(forecast)
Registered S3 method overwritten by 'quantmod':
  method            from
  as.zoo.data.frame zoo 
This is forecast 8.21.1 
  Want to meet other forecasters? Join the International Institute of Forecasters:
  http://forecasters.org/
library(DIMORA)
Loading required package: minpack.lm
Loading required package: numDeriv
Loading required package: reshape2
Loading required package: deSolve
library(fpp2)
── Attaching packages ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── fpp2 2.5 ──
✔ ggplot2   3.4.4     ✔ expsmooth 2.3  
✔ fma       2.5       
library(ggplot2)
library(plotly)

Attaching package: ‘plotly’

The following object is masked from ‘package:ggplot2’:

    last_plot

The following object is masked from ‘package:stats’:

    filter

The following object is masked from ‘package:graphics’:

    layout
library(tseries)

    ‘tseries’ version: 0.10-55

    ‘tseries’ is a package for time series analysis and computational finance.

    See ‘library(help="tseries")’ for details.
library(randomForest)
randomForest 4.7-1.1
Type rfNews() to see new features/changes/bug fixes.

Attaching package: ‘randomForest’

The following object is masked from ‘package:ggplot2’:

    margin
library(BASS)
library(Metrics) 

Attaching package: ‘Metrics’

The following object is masked from ‘package:forecast’:

    accuracy
library(dplyr)

Attaching package: ‘dplyr’

The following object is masked from ‘package:randomForest’:

    combine

The following objects are masked from ‘package:stats’:

    filter, lag

The following objects are masked from ‘package:base’:

    intersect, setdiff, setequal, union
library(tibble)
# folder_path = "~/Desktop/unipd/time-series/project/data/"
folder_path = "./data/"

# shares_file <- paste0(folder_path, "us-total-share-prices.csv")
# m2_file <- paste0(folder_path, "us-M2.csv")
# usir_file <- paste0(folder_path, "us-interest-rate.csv")
euir_file <- paste0(folder_path, "eu-interest-rate.csv")
# uscpi_file <- paste0(folder_path, "us-consumer-price-index.csv")
eucpi_file <- paste0(folder_path, "eu-consumer-price-index.csv")
rate_file <- paste0(folder_path, "euro-daily-hist_1999_2022.csv")
  

# shares<- read.csv(shares_file)
# usm2 <- read.csv(m2_file)
# usir <- read.csv(usir_file)
euir <- read.csv(euir_file)
# uscpi <- read.csv(uscpi_file)
eucpi <- read.csv(eucpi_file)
rate <- read.csv(rate_file)
# ### Looking at share price
# shares$DATE <- as.Date(shares$DATE)
# 
# # plot(shares$DATE, shares$SPASTT01USM661N, xlab = "Time", ylab = "Price")
# gg <- ggplot(shares, aes(x = DATE, y = SPASTT01USM661N))+
#   geom_line(aes(group = 1), color = "blue", linewidth = 0.5) +
#   geom_point() +
#   labs(
#     title = "Total Share Prices Change with Time",
#     x = "Time",
#     y = "Index 2015 = 100"
#   ) +
#   scale_x_date(date_labels = "%Y-%m", date_breaks = "1 year") +
#   theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1))
# 
# plotly_gg <- ggplotly(gg)
# 
# plotly_gg
### There is a clear increasing trend that is confirmed by the ACF being the highest
### at the smaller lags
# ggAcf(shares$SPASTT01USM661N, lag = 760)
### Attempting modeling share prices with Naive Method
# nmshares_model <- meanf(shares$SPASTT01USM661N, h = 1)
# plot(nmshares_model)
## Checking residuals
# nmshares_res <- residuals(nmshares_model)
# autoplot(ts(nmshares_res)) + xlab("Month") + ylab("") +
#   ggtitle("Residuals from Naive Method")
# gg_hist <- gghistogram(nmshares_res) + ggtitle("Histogram of Naive Method Residuals")
# ggplotly(gg_hist)
# ggAcf(nmshares_res, lag = 760)
### Attempting modeling share prices with linear regression model
# shares_lm = lm(shares$SPASTT01USM661N~ shares$DATE)
# summary(shares_lm) ###R^2 value of 83.96%
# tt <- 1:NROW(shares)
# plot(tt, shares$SPASTT01USM661N, xlab="Time", ylab="Index 2015 = 100", type = "p")
# abline(shares_lm)
head(euir)

euir$DATE <- as.Date(euir$DATE)
euir_df <- data.frame(date = euir$DATE, interest_rate = euir$IRSTCI01EZM156N)
summary(euir_df$interest_rate)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
-0.5847 -0.0506  2.0518  1.9590  3.7900  6.8400 
ggplot(euir_df, aes(x = date, y = interest_rate)) +
  geom_line() +
  labs(title = "Euro Interest Rate Over Time",
       x = "Date", y = "Interest Rate")

# Check for Stationarity (Augmented Dickey-Fuller test)
adf.test(euir_df$interest_rate)

    Augmented Dickey-Fuller Test

data:  euir_df$interest_rate
Dickey-Fuller = -2.0654, Lag order = 7, p-value = 0.5493
alternative hypothesis: stationary
# Autocorrelation and Partial Autocorrelation Plots
ggtsdisplay(euir_df$interest_rate, lag = 900)

ggAcf(euir_df$interest_rate, lag.max = 700)

Linear regression model

train_data <- euir_df[1:(nrow(euir_df) - 3), ]
test_data <- euir_df[(nrow(euir_df) - 2):nrow(euir_df), ]

model <- lm(interest_rate ~ date, data = train_data)

summary(model)

Call:
lm(formula = interest_rate ~ date, data = train_data)

Residuals:
    Min      1Q  Median      3Q     Max 
-1.4724 -0.6525 -0.3006  0.3833  4.4335 

Coefficients:
              Estimate Std. Error t value Pr(>|t|)    
(Intercept)  1.030e+01  2.503e-01   41.16   <2e-16 ***
date        -5.909e-04  1.729e-05  -34.18   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 1.012 on 352 degrees of freedom
Multiple R-squared:  0.7685,    Adjusted R-squared:  0.7678 
F-statistic:  1168 on 1 and 352 DF,  p-value: < 2.2e-16
predictions <- predict(model, newdata = test_data)
residuals <- residuals(model)
residuals_test <- residuals(model)[1:nrow(test_data)]
rmse_value <- rmse(predictions, test_data$interest_rate)
mae_value <- mae(predictions, test_data$interest_rate)

cat("RMSE:", rmse_value, "\n")
RMSE: 4.842428 
cat("MAE:", mae_value, "\n")
MAE: 4.840305 
plot(test_data$date, test_data$interest_rate, col = "blue", type = "l", xlab = "Date", ylab = "Interest Rate")
lines(test_data$date, predictions, col = "red")
legend("topleft", legend = c("Actual", "Predicted"), col = c("blue", "red"), lty = 1)

# Residuals plot
plot(test_data$date, residuals_test, col = "green", type = "l", xlab = "Date", ylab = "Residuals")


plot(train_data$date, residuals, col = "green", type = "l", xlab = "Date", ylab = "Residuals train")

result_table <- data.frame(
  Date = test_data$date,
  Actual = test_data$interest_rate,
  Predicted = predictions
)

print(result_table)
dwtest(model)

    Durbin-Watson test

data:  model
DW = 0.029354, p-value < 2.2e-16
alternative hypothesis: true autocorrelation is greater than 0
ARIMA
```r train_data <- euir_df[1:(nrow(euir_df) - 3), ] test_data <- euir_df[(nrow(euir_df) - 2):nrow(euir_df), ]
arima_model <- auto.arima(train_data$interest_rate)
summary(arima_model) ```
``` Series: train_data$interest_rate ARIMA(0,2,1)
Coefficients: ma1 -0.7618 s.e. 0.0400
sigma^2 = 0.02614: log likelihood = 142 AIC=-279.99 AICc=-279.96 BIC=-272.26
Training set error measures: ME RMSE MAE MPE MAPE MASE ACF1 Training set 0.004576847 0.1609926 0.09655125 -0.5811529 11.74419 0.9702804 -0.03495108 ```
r # Forecasting for the next 3 months arima_forecast <- forecast(arima_model, h = 3) print(arima_forecast)
r arima_pred_values <- arima_forecast$mean
r # Plotting ARIMA Forecast plot(arima_forecast, xlab = "Date", ylab = "Interest Rate Forecast")
r # ARIMA residuals arima_residuals <- residuals(arima_model) plot(train_data$date, arima_residuals, col = "green", type = "l", xlab = "Date", ylab = "ARIMA Residuals")
r forecasted_values <- arima_forecast$mean actual_values <- as.numeric(test_data$interest_rate) arima_residuals_test <- actual_values - forecasted_values
```r #ARIMA forecasted values and residuals arima_table <- data.frame( Date = index(test_data), Actual_Values = actual_values, Forecasted_Interest_Rate = forecasted_values, Residuals = arima_residuals_test )
# Print the ARIMA forecast and residuals table print(arima_table) ```
r NA
r # Plotting ARIMA predicted values and residuals for the test data plot(test_data$date, arima_pred_values, col = "blue", type = "l", xlab = "Date", ylab = "ARIMA Predicted Values") lines(test_data$date, arima_residuals_test, col = "green")
```r legend(“topright”, legend = c(“Predicted Values”, “Residuals”), col = c(“blue”, “green”), lty = 1)
```
head(eucpi)

eucpi$DATE <- as.Date(eucpi$DATE)
eucpi_df <- data.frame(date = eucpi$DATE, cp_index = eucpi$EA19CPALTT01GYM)
summary(eucpi_df$cp_index)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  -0.60    1.30    2.10    2.19    2.60   10.60 
ggplot(eucpi_df, aes(x = date, y = cp_index)) +
  geom_line() +
  labs(title = "Euro CP Index Over Time",
       x = "Date", y = "Index Rate")

# Check for Stationarity (Augmented Dickey-Fuller test)
adf.test(eucpi_df$cp_index)

    Augmented Dickey-Fuller Test

data:  eucpi_df$cp_index
Dickey-Fuller = -3.6941, Lag order = 7, p-value = 0.02453
alternative hypothesis: stationary
# Autocorrelation and Partial Autocorrelation Plots
ggtsdisplay(eucpi_df$cp_index, lag = 900)

ggAcf(eucpi_df$cp_index, lag.max = 700)

LINEAR REGRESSION

train_data <- eucpi_df[1:(nrow(eucpi_df) - 3), ]
test_data <- eucpi_df[(nrow(eucpi_df) - 2):nrow(eucpi_df), ]

model <- lm(cp_index ~ date, data = train_data)

summary(model)

Call:
lm(formula = cp_index ~ date, data = train_data)

Residuals:
    Min      1Q  Median      3Q     Max 
-2.6457 -0.7591 -0.1226  0.3644  8.9977 

Coefficients:
              Estimate Std. Error t value Pr(>|t|)    
(Intercept)  3.368e+00  3.097e-01  10.875  < 2e-16 ***
date        -9.163e-05  2.231e-05  -4.107 4.91e-05 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 1.464 on 380 degrees of freedom
Multiple R-squared:  0.0425,    Adjusted R-squared:  0.03998 
F-statistic: 16.87 on 1 and 380 DF,  p-value: 4.912e-05
predictions <- predict(model, newdata = test_data)
residuals <- residuals(model)
residuals_test <- residuals(model)[1:nrow(test_data)]
rmse_value <- rmse(predictions, test_data$cp_index)
mae_value <- mae(predictions, test_data$cp_index)

cat("RMSE:", rmse_value, "\n")
RMSE: 7.75817 
cat("MAE:", mae_value, "\n")
MAE: 7.736682 
plot(test_data$date, test_data$cp_index, col = "blue", type = "l", xlab = "Date", ylab = "EU Consumer Price INdex")
lines(test_data$date, predictions, col = "red")
legend("topleft", legend = c("Actual", "Predicted"), col = c("blue", "red"), lty = 1)

# Residuals plot
plot(test_data$date, residuals_test, col = "green", type = "l", xlab = "Date", ylab = "Residuals test")


plot(train_data$date, residuals, col = "green", type = "l", xlab = "Date", ylab = "Residuals train")

result_table <- data.frame(
  Date = test_data$date,
  Actual = test_data$cp_index,
  Predicted = predictions
)

print(result_table)
dwtest(model)

    Durbin-Watson test

data:  model
DW = 0.036438, p-value < 2.2e-16
alternative hypothesis: true autocorrelation is greater than 0

ARIMA (CP Index)

train_data <- eucpi_df[1:(nrow(eucpi_df) - 3), ]
test_data <- eucpi_df[(nrow(eucpi_df) - 2):nrow(eucpi_df), ]

arima_model <- auto.arima(train_data$cp_index)

summary(arima_model)
Series: train_data$cp_index 
ARIMA(1,2,1) 

Coefficients:
         ar1      ma1
      0.0681  -0.8891
s.e.  0.0620   0.0339

sigma^2 = 0.07102:  log likelihood = -36.41
AIC=78.82   AICc=78.89   BIC=90.64

Training set error measures:
                     ME      RMSE       MAE MPE MAPE      MASE         ACF1
Training set 0.00887859 0.2651023 0.1945961 Inf  Inf 0.9980657 -0.002914337
# Forecasting for the next 3 months
arima_forecast <- forecast(arima_model, h = 3)
arima_pred_values <- arima_forecast$mean
print(arima_forecast)
# Plotting ARIMA Forecast
plot(arima_forecast, xlab = "Date", ylab = "CP Index Forecast")

# ARIMA residuals
arima_residuals <- residuals(arima_model)
plot(train_data$date, arima_residuals, col = "green", type = "l", xlab = "Date", ylab = "ARIMA Residuals")

forecasted_values <- arima_forecast$mean
actual_values <- as.numeric(test_data$cp_index)
arima_residuals_test <- actual_values - forecasted_values
#ARIMA forecasted values and residuals
arima_table <- data.frame(
  Date = index(test_data),
  Actual_Values = actual_values,
  Forecasted_CP_Index = forecasted_values,
  Residuals = arima_residuals_test
)

# Print the ARIMA forecast and residuals table
print(arima_table)
NA
# Plotting ARIMA predicted values and residuals for the test data
plot(test_data$date, arima_pred_values, col = "blue", type = "l", xlab = "Date", ylab = "ARIMA Predicted Values")
lines(test_data$date, arima_residuals_test, col = "green")
legend("topright", legend = c("Predicted Values", "Residuals"), col = c("blue", "green"), lty = 1)


US RATE

rate$Period.Unit. <- as.Date(rate$Period.Unit.)
rate_df <- data.frame(Date = rate$Period.Unit., US_rate = rate$X.US.dollar..)
rate_df <- rate_df %>% 
  mutate(Date = format(Date, "%Y-%m"))
head(rate_df)
filtered_data <- rate_df %>%
  group_by(Date) %>%
  summarize(mean_rate = mean(as.numeric(US_rate)))
Warning: There were 42 warnings in `summarize()`.
The first warning was:
ℹ In argument: `mean_rate = mean(as.numeric(US_rate))`.
ℹ In group 12: `Date = "1999-12"`.
Caused by warning in `mean()`:
! NAs introduced by coercion
ℹ Run ]8;;ide:run:dplyr::last_dplyr_warnings()dplyr::last_dplyr_warnings()]8;; to see the 41 remaining warnings.
filtered_data$Date <- paste0(filtered_data$Date, '-01')
filtered_data$Date <- as.Date(filtered_data$Date)
head(filtered_data)
summary(filtered_data$mean_rate)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    NA's 
 0.8532  1.0982  1.1786  1.1900  1.2987  1.5770      42 
ggplot(filtered_data, aes(x = Date, y = mean_rate)) +
  geom_line() +
  labs(
    title = "US Rate Over Time",
    x = "Time", 
    y = "US Rate"
    )

gg <- ggplot(filtered_data, aes(x = Date, y = mean_rate))+
  geom_line(aes(group = 1), color = "blue", linewidth = 0.5) +
  geom_point() +
  labs(
    title = "US Rate",
    x = "Time",
    y = "Rate"
  ) +
  scale_x_date(date_labels = "%Y-%m", date_breaks = "1 year") +
  theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1))

plotly_gg <- ggplotly(gg)

plotly_gg
str(euir_df)
'data.frame':   357 obs. of  2 variables:
 $ date         : Date, format: "1994-01-01" "1994-02-01" "1994-03-01" "1994-04-01" ...
 $ interest_rate: num  6.84 6.73 6.68 6.22 5.85 6.32 6.27 5.94 6 6.13 ...
str(filtered_data)
tibble [293 × 2] (S3: tbl_df/tbl/data.frame)
 $ Date     : Date[1:293], format: "1999-01-01" "1999-02-01" "1999-03-01" "1999-04-01" ...
 $ mean_rate: num [1:293] 1.16 1.12 1.09 1.07 1.06 ...
merged_df <- merge(merge(euir_df, eucpi_df, by = "date"), filtered_data, by.x = "date", by.y = "Date")
names(merged_df) <- c("date", "eu_ir", "eu_cpi", "us_rate")
head(merged_df)
tail(merged_df)

ARIMAX (US rate + EU interest rate)

merged_df$date <- as.Date(merged_df$date)
us_ts <- ts(merged_df$us_rate, frequency = 12, start = c(1999, 1))
# Split the data into train and test sets
train_data <- window(us_ts, start = c(1999, 1), end = c(2022, 10))
test_data <- window(us_ts, start = c(2022, 11))

external_regressor <- merged_df$eu_ir[merged_df$date >= as.Date("1999-01-01") & merged_df$date <= as.Date("2022-10-01")]
fit <- Arima(train_data, order = c(1, 0, 1), xreg = external_regressor)

forecast_values <- forecast(fit, xreg = merged_df$eu_ir[merged_df$date >= as.Date("2022-11-01")], h = 3)
predicted_values <- forecast_values$mean
actual_values <- window(us_ts, start = c(2022, 11))
result_table <- tibble(
  Date = time(actual_values),
  Actual_Values = actual_values,
  Predicted_Values = predicted_values
)
plot(forecast_values, main = "ARIMAX Forecast for US Rate")
lines(actual_values, col = "blue")
legend("topleft", legend = c("Actual", "Forecast"), col = c("blue", "black"), lty = 1)

print(result_table)

ARIMAX (US rate + EU Consumer Price Index)

# Split the data into train and test sets
train_data <- window(us_ts, start = c(1999, 1), end = c(2022, 10))
test_data <- window(us_ts, start = c(2022, 11))

external_regressor <- merged_df$eu_cpi[merged_df$date >= as.Date("1999-01-01") & merged_df$date <= as.Date("2022-10-01")]
fit <- Arima(train_data, order = c(1, 0, 1), xreg = external_regressor)

forecast_values <- forecast(fit, xreg = merged_df$eu_cpi[merged_df$date >= as.Date("2022-11-01")], h = 3)
predicted_values <- forecast_values$mean
actual_values <- window(us_ts, start = c(2022, 11))
result_table <- tibble(
  Date = time(actual_values),
  Actual_Values = actual_values,
  Predicted_Values = predicted_values
)
plot(forecast_values, main = "ARIMAX Forecast for US Rate")
lines(actual_values, col = "blue")
legend("topleft", legend = c("Actual", "Forecast"), col = c("blue", "black"), lty = 1)

print(result_table)
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKVGhpcyBpcyBhbiBbUiBNYXJrZG93bl0oaHR0cDovL3JtYXJrZG93bi5yc3R1ZGlvLmNvbSkgTm90ZWJvb2suIFdoZW4geW91IGV4ZWN1dGUgY29kZSB3aXRoaW4gdGhlIG5vdGVib29rLCB0aGUgcmVzdWx0cyBhcHBlYXIgYmVuZWF0aCB0aGUgY29kZS4gCgpUcnkgZXhlY3V0aW5nIHRoaXMgY2h1bmsgYnkgY2xpY2tpbmcgdGhlICpSdW4qIGJ1dHRvbiB3aXRoaW4gdGhlIGNodW5rIG9yIGJ5IHBsYWNpbmcgeW91ciBjdXJzb3IgaW5zaWRlIGl0IGFuZCBwcmVzc2luZyAqQ3RybCtTaGlmdCtFbnRlciouIAoKYGBge3J9CmxpYnJhcnkocmVhZHhsKQpsaWJyYXJ5KGxtdGVzdCkgCmxpYnJhcnkoZm9yZWNhc3QpCmxpYnJhcnkoRElNT1JBKQpsaWJyYXJ5KGZwcDIpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShwbG90bHkpCmxpYnJhcnkodHNlcmllcykKbGlicmFyeShyYW5kb21Gb3Jlc3QpCmxpYnJhcnkoQkFTUykKbGlicmFyeShNZXRyaWNzKSAKbGlicmFyeShkcGx5cikKbGlicmFyeSh0aWJibGUpCmBgYAoKCmBgYHtyfQojIGZvbGRlcl9wYXRoID0gIn4vRGVza3RvcC91bmlwZC90aW1lLXNlcmllcy9wcm9qZWN0L2RhdGEvIgpmb2xkZXJfcGF0aCA9ICIuL2RhdGEvIgoKIyBzaGFyZXNfZmlsZSA8LSBwYXN0ZTAoZm9sZGVyX3BhdGgsICJ1cy10b3RhbC1zaGFyZS1wcmljZXMuY3N2IikKIyBtMl9maWxlIDwtIHBhc3RlMChmb2xkZXJfcGF0aCwgInVzLU0yLmNzdiIpCiMgdXNpcl9maWxlIDwtIHBhc3RlMChmb2xkZXJfcGF0aCwgInVzLWludGVyZXN0LXJhdGUuY3N2IikKZXVpcl9maWxlIDwtIHBhc3RlMChmb2xkZXJfcGF0aCwgImV1LWludGVyZXN0LXJhdGUuY3N2IikKIyB1c2NwaV9maWxlIDwtIHBhc3RlMChmb2xkZXJfcGF0aCwgInVzLWNvbnN1bWVyLXByaWNlLWluZGV4LmNzdiIpCmV1Y3BpX2ZpbGUgPC0gcGFzdGUwKGZvbGRlcl9wYXRoLCAiZXUtY29uc3VtZXItcHJpY2UtaW5kZXguY3N2IikKcmF0ZV9maWxlIDwtIHBhc3RlMChmb2xkZXJfcGF0aCwgImV1cm8tZGFpbHktaGlzdF8xOTk5XzIwMjIuY3N2IikKICAKCiMgc2hhcmVzPC0gcmVhZC5jc3Yoc2hhcmVzX2ZpbGUpCiMgdXNtMiA8LSByZWFkLmNzdihtMl9maWxlKQojIHVzaXIgPC0gcmVhZC5jc3YodXNpcl9maWxlKQpldWlyIDwtIHJlYWQuY3N2KGV1aXJfZmlsZSkKIyB1c2NwaSA8LSByZWFkLmNzdih1c2NwaV9maWxlKQpldWNwaSA8LSByZWFkLmNzdihldWNwaV9maWxlKQpyYXRlIDwtIHJlYWQuY3N2KHJhdGVfZmlsZSkKYGBgCgpgYGB7cn0KIyAjIyMgTG9va2luZyBhdCBzaGFyZSBwcmljZQojIHNoYXJlcyREQVRFIDwtIGFzLkRhdGUoc2hhcmVzJERBVEUpCiMgCiMgIyBwbG90KHNoYXJlcyREQVRFLCBzaGFyZXMkU1BBU1RUMDFVU002NjFOLCB4bGFiID0gIlRpbWUiLCB5bGFiID0gIlByaWNlIikKIyBnZyA8LSBnZ3Bsb3Qoc2hhcmVzLCBhZXMoeCA9IERBVEUsIHkgPSBTUEFTVFQwMVVTTTY2MU4pKSsKIyAgIGdlb21fbGluZShhZXMoZ3JvdXAgPSAxKSwgY29sb3IgPSAiYmx1ZSIsIGxpbmV3aWR0aCA9IDAuNSkgKwojICAgZ2VvbV9wb2ludCgpICsKIyAgIGxhYnMoCiMgICAgIHRpdGxlID0gIlRvdGFsIFNoYXJlIFByaWNlcyBDaGFuZ2Ugd2l0aCBUaW1lIiwKIyAgICAgeCA9ICJUaW1lIiwKIyAgICAgeSA9ICJJbmRleCAyMDE1ID0gMTAwIgojICAgKSArCiMgICBzY2FsZV94X2RhdGUoZGF0ZV9sYWJlbHMgPSAiJVktJW0iLCBkYXRlX2JyZWFrcyA9ICIxIHllYXIiKSArCiMgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCB2anVzdCA9IDAuNSwgaGp1c3Q9MSkpCiMgCiMgcGxvdGx5X2dnIDwtIGdncGxvdGx5KGdnKQojIAojIHBsb3RseV9nZwpgYGAKCmBgYHtyfQojIyMgVGhlcmUgaXMgYSBjbGVhciBpbmNyZWFzaW5nIHRyZW5kIHRoYXQgaXMgY29uZmlybWVkIGJ5IHRoZSBBQ0YgYmVpbmcgdGhlIGhpZ2hlc3QKIyMjIGF0IHRoZSBzbWFsbGVyIGxhZ3MKIyBnZ0FjZihzaGFyZXMkU1BBU1RUMDFVU002NjFOLCBsYWcgPSA3NjApCmBgYAoKCmBgYHtyfQojIyMgQXR0ZW1wdGluZyBtb2RlbGluZyBzaGFyZSBwcmljZXMgd2l0aCBOYWl2ZSBNZXRob2QKIyBubXNoYXJlc19tb2RlbCA8LSBtZWFuZihzaGFyZXMkU1BBU1RUMDFVU002NjFOLCBoID0gMSkKIyBwbG90KG5tc2hhcmVzX21vZGVsKQpgYGAKCgpgYGB7cn0KIyMgQ2hlY2tpbmcgcmVzaWR1YWxzCiMgbm1zaGFyZXNfcmVzIDwtIHJlc2lkdWFscyhubXNoYXJlc19tb2RlbCkKIyBhdXRvcGxvdCh0cyhubXNoYXJlc19yZXMpKSArIHhsYWIoIk1vbnRoIikgKyB5bGFiKCIiKSArCiMgICBnZ3RpdGxlKCJSZXNpZHVhbHMgZnJvbSBOYWl2ZSBNZXRob2QiKQojIGdnX2hpc3QgPC0gZ2doaXN0b2dyYW0obm1zaGFyZXNfcmVzKSArIGdndGl0bGUoIkhpc3RvZ3JhbSBvZiBOYWl2ZSBNZXRob2QgUmVzaWR1YWxzIikKIyBnZ3Bsb3RseShnZ19oaXN0KQpgYGAKCgpgYGB7cn0KIyBnZ0FjZihubXNoYXJlc19yZXMsIGxhZyA9IDc2MCkKYGBgCgoKYGBge3J9CiMjIyBBdHRlbXB0aW5nIG1vZGVsaW5nIHNoYXJlIHByaWNlcyB3aXRoIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsCiMgc2hhcmVzX2xtID0gbG0oc2hhcmVzJFNQQVNUVDAxVVNNNjYxTn4gc2hhcmVzJERBVEUpCiMgc3VtbWFyeShzaGFyZXNfbG0pICMjI1JeMiB2YWx1ZSBvZiA4My45NiUKYGBgCgpgYGB7cn0KIyB0dCA8LSAxOk5ST1coc2hhcmVzKQojIHBsb3QodHQsIHNoYXJlcyRTUEFTVFQwMVVTTTY2MU4sIHhsYWI9IlRpbWUiLCB5bGFiPSJJbmRleCAyMDE1ID0gMTAwIiwgdHlwZSA9ICJwIikKIyBhYmxpbmUoc2hhcmVzX2xtKQpgYGAKCgpgYGB7cn0KaGVhZChldWlyKQoKZXVpciREQVRFIDwtIGFzLkRhdGUoZXVpciREQVRFKQpldWlyX2RmIDwtIGRhdGEuZnJhbWUoZGF0ZSA9IGV1aXIkREFURSwgaW50ZXJlc3RfcmF0ZSA9IGV1aXIkSVJTVENJMDFFWk0xNTZOKQpgYGAKCgoKYGBge3J9CnN1bW1hcnkoZXVpcl9kZiRpbnRlcmVzdF9yYXRlKQpgYGAKCgpgYGB7cn0KZ2dwbG90KGV1aXJfZGYsIGFlcyh4ID0gZGF0ZSwgeSA9IGludGVyZXN0X3JhdGUpKSArCiAgZ2VvbV9saW5lKCkgKwogIGxhYnModGl0bGUgPSAiRXVybyBJbnRlcmVzdCBSYXRlIE92ZXIgVGltZSIsCiAgICAgICB4ID0gIkRhdGUiLCB5ID0gIkludGVyZXN0IFJhdGUiKQpgYGAKCgpgYGB7cn0KIyBDaGVjayBmb3IgU3RhdGlvbmFyaXR5IChBdWdtZW50ZWQgRGlja2V5LUZ1bGxlciB0ZXN0KQphZGYudGVzdChldWlyX2RmJGludGVyZXN0X3JhdGUpCmBgYAoKYGBge3J9CiMgQXV0b2NvcnJlbGF0aW9uIGFuZCBQYXJ0aWFsIEF1dG9jb3JyZWxhdGlvbiBQbG90cwpnZ3RzZGlzcGxheShldWlyX2RmJGludGVyZXN0X3JhdGUsIGxhZyA9IDkwMCkKYGBgCgoKYGBge3J9CmdnQWNmKGV1aXJfZGYkaW50ZXJlc3RfcmF0ZSwgbGFnLm1heCA9IDcwMCkKYGBgCgpMaW5lYXIgcmVncmVzc2lvbiBtb2RlbAoKYGBge3J9CnRyYWluX2RhdGEgPC0gZXVpcl9kZlsxOihucm93KGV1aXJfZGYpIC0gMyksIF0KdGVzdF9kYXRhIDwtIGV1aXJfZGZbKG5yb3coZXVpcl9kZikgLSAyKTpucm93KGV1aXJfZGYpLCBdCgptb2RlbCA8LSBsbShpbnRlcmVzdF9yYXRlIH4gZGF0ZSwgZGF0YSA9IHRyYWluX2RhdGEpCgpzdW1tYXJ5KG1vZGVsKQoKYGBgCgpgYGB7cn0KcHJlZGljdGlvbnMgPC0gcHJlZGljdChtb2RlbCwgbmV3ZGF0YSA9IHRlc3RfZGF0YSkKcmVzaWR1YWxzIDwtIHJlc2lkdWFscyhtb2RlbCkKcmVzaWR1YWxzX3Rlc3QgPC0gcmVzaWR1YWxzKG1vZGVsKVsxOm5yb3codGVzdF9kYXRhKV0KYGBgCgpgYGB7cn0Kcm1zZV92YWx1ZSA8LSBybXNlKHByZWRpY3Rpb25zLCB0ZXN0X2RhdGEkaW50ZXJlc3RfcmF0ZSkKbWFlX3ZhbHVlIDwtIG1hZShwcmVkaWN0aW9ucywgdGVzdF9kYXRhJGludGVyZXN0X3JhdGUpCgpjYXQoIlJNU0U6Iiwgcm1zZV92YWx1ZSwgIlxuIikKY2F0KCJNQUU6IiwgbWFlX3ZhbHVlLCAiXG4iKQpgYGAKYGBge3J9CnBsb3QodGVzdF9kYXRhJGRhdGUsIHRlc3RfZGF0YSRpbnRlcmVzdF9yYXRlLCBjb2wgPSAiYmx1ZSIsIHR5cGUgPSAibCIsIHhsYWIgPSAiRGF0ZSIsIHlsYWIgPSAiSW50ZXJlc3QgUmF0ZSIpCmxpbmVzKHRlc3RfZGF0YSRkYXRlLCBwcmVkaWN0aW9ucywgY29sID0gInJlZCIpCmxlZ2VuZCgidG9wbGVmdCIsIGxlZ2VuZCA9IGMoIkFjdHVhbCIsICJQcmVkaWN0ZWQiKSwgY29sID0gYygiYmx1ZSIsICJyZWQiKSwgbHR5ID0gMSkKYGBgCgoKYGBge3J9CiMgUmVzaWR1YWxzIHBsb3QKcGxvdCh0ZXN0X2RhdGEkZGF0ZSwgcmVzaWR1YWxzX3Rlc3QsIGNvbCA9ICJncmVlbiIsIHR5cGUgPSAibCIsIHhsYWIgPSAiRGF0ZSIsIHlsYWIgPSAiUmVzaWR1YWxzIikKCnBsb3QodHJhaW5fZGF0YSRkYXRlLCByZXNpZHVhbHMsIGNvbCA9ICJncmVlbiIsIHR5cGUgPSAibCIsIHhsYWIgPSAiRGF0ZSIsIHlsYWIgPSAiUmVzaWR1YWxzIHRyYWluIikKYGBgCgoKYGBge3J9CnJlc3VsdF90YWJsZSA8LSBkYXRhLmZyYW1lKAogIERhdGUgPSB0ZXN0X2RhdGEkZGF0ZSwKICBBY3R1YWwgPSB0ZXN0X2RhdGEkaW50ZXJlc3RfcmF0ZSwKICBQcmVkaWN0ZWQgPSBwcmVkaWN0aW9ucwopCgpwcmludChyZXN1bHRfdGFibGUpCmBgYApgYGB7cn0KZHd0ZXN0KG1vZGVsKQpgYGAKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCkFSSU1BCgpgYGB7cn0KdHJhaW5fZGF0YSA8LSBldWlyX2RmWzE6KG5yb3coZXVpcl9kZikgLSAzKSwgXQp0ZXN0X2RhdGEgPC0gZXVpcl9kZlsobnJvdyhldWlyX2RmKSAtIDIpOm5yb3coZXVpcl9kZiksIF0KCmFyaW1hX21vZGVsIDwtIGF1dG8uYXJpbWEodHJhaW5fZGF0YSRpbnRlcmVzdF9yYXRlKQoKc3VtbWFyeShhcmltYV9tb2RlbCkKYGBgCgoKYGBge3J9CiMgRm9yZWNhc3RpbmcgZm9yIHRoZSBuZXh0IDMgbW9udGhzCmFyaW1hX2ZvcmVjYXN0IDwtIGZvcmVjYXN0KGFyaW1hX21vZGVsLCBoID0gMykKcHJpbnQoYXJpbWFfZm9yZWNhc3QpCmBgYApgYGB7cn0KYXJpbWFfcHJlZF92YWx1ZXMgPC0gYXJpbWFfZm9yZWNhc3QkbWVhbgpgYGAKCgpgYGB7cn0KIyBQbG90dGluZyBBUklNQSBGb3JlY2FzdApwbG90KGFyaW1hX2ZvcmVjYXN0LCB4bGFiID0gIkRhdGUiLCB5bGFiID0gIkludGVyZXN0IFJhdGUgRm9yZWNhc3QiKQpgYGAKYGBge3J9CiMgQVJJTUEgcmVzaWR1YWxzCmFyaW1hX3Jlc2lkdWFscyA8LSByZXNpZHVhbHMoYXJpbWFfbW9kZWwpCnBsb3QodHJhaW5fZGF0YSRkYXRlLCBhcmltYV9yZXNpZHVhbHMsIGNvbCA9ICJncmVlbiIsIHR5cGUgPSAibCIsIHhsYWIgPSAiRGF0ZSIsIHlsYWIgPSAiQVJJTUEgUmVzaWR1YWxzIikKYGBgCmBgYHtyfQpmb3JlY2FzdGVkX3ZhbHVlcyA8LSBhcmltYV9mb3JlY2FzdCRtZWFuCmFjdHVhbF92YWx1ZXMgPC0gYXMubnVtZXJpYyh0ZXN0X2RhdGEkaW50ZXJlc3RfcmF0ZSkKYXJpbWFfcmVzaWR1YWxzX3Rlc3QgPC0gYWN0dWFsX3ZhbHVlcyAtIGZvcmVjYXN0ZWRfdmFsdWVzCmBgYAoKCmBgYHtyfQojQVJJTUEgZm9yZWNhc3RlZCB2YWx1ZXMgYW5kIHJlc2lkdWFscwphcmltYV90YWJsZSA8LSBkYXRhLmZyYW1lKAogIERhdGUgPSBpbmRleCh0ZXN0X2RhdGEpLAogIEFjdHVhbF9WYWx1ZXMgPSBhY3R1YWxfdmFsdWVzLAogIEZvcmVjYXN0ZWRfSW50ZXJlc3RfUmF0ZSA9IGZvcmVjYXN0ZWRfdmFsdWVzLAogIFJlc2lkdWFscyA9IGFyaW1hX3Jlc2lkdWFsc190ZXN0CikKCiMgUHJpbnQgdGhlIEFSSU1BIGZvcmVjYXN0IGFuZCByZXNpZHVhbHMgdGFibGUKcHJpbnQoYXJpbWFfdGFibGUpCgpgYGAKCgoKYGBge3J9CiMgUGxvdHRpbmcgQVJJTUEgcHJlZGljdGVkIHZhbHVlcyBhbmQgcmVzaWR1YWxzIGZvciB0aGUgdGVzdCBkYXRhCnBsb3QodGVzdF9kYXRhJGRhdGUsIGFyaW1hX3ByZWRfdmFsdWVzLCBjb2wgPSAiYmx1ZSIsIHR5cGUgPSAibCIsIHhsYWIgPSAiRGF0ZSIsIHlsYWIgPSAiQVJJTUEgUHJlZGljdGVkIFZhbHVlcyIpCmxpbmVzKHRlc3RfZGF0YSRkYXRlLCBhcmltYV9yZXNpZHVhbHNfdGVzdCwgY29sID0gImdyZWVuIikKbGVnZW5kKCJ0b3ByaWdodCIsIGxlZ2VuZCA9IGMoIlByZWRpY3RlZCBWYWx1ZXMiLCAiUmVzaWR1YWxzIiksIGNvbCA9IGMoImJsdWUiLCAiZ3JlZW4iKSwgbHR5ID0gMSkKCmBgYAoKCmBgYHtyfQpgYGAKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQpgYGB7cn0KaGVhZChldWNwaSkKCmV1Y3BpJERBVEUgPC0gYXMuRGF0ZShldWNwaSREQVRFKQpldWNwaV9kZiA8LSBkYXRhLmZyYW1lKGRhdGUgPSBldWNwaSREQVRFLCBjcF9pbmRleCA9IGV1Y3BpJEVBMTlDUEFMVFQwMUdZTSkKYGBgCgoKYGBge3J9CnN1bW1hcnkoZXVjcGlfZGYkY3BfaW5kZXgpCmBgYAoKCmBgYHtyfQpnZ3Bsb3QoZXVjcGlfZGYsIGFlcyh4ID0gZGF0ZSwgeSA9IGNwX2luZGV4KSkgKwogIGdlb21fbGluZSgpICsKICBsYWJzKHRpdGxlID0gIkV1cm8gQ1AgSW5kZXggT3ZlciBUaW1lIiwKICAgICAgIHggPSAiRGF0ZSIsIHkgPSAiSW5kZXggUmF0ZSIpCmBgYAoKCmBgYHtyfQojIENoZWNrIGZvciBTdGF0aW9uYXJpdHkgKEF1Z21lbnRlZCBEaWNrZXktRnVsbGVyIHRlc3QpCmFkZi50ZXN0KGV1Y3BpX2RmJGNwX2luZGV4KQpgYGAKCgpgYGB7cn0KIyBBdXRvY29ycmVsYXRpb24gYW5kIFBhcnRpYWwgQXV0b2NvcnJlbGF0aW9uIFBsb3RzCmdndHNkaXNwbGF5KGV1Y3BpX2RmJGNwX2luZGV4LCBsYWcgPSA5MDApCmBgYAoKYGBge3J9CmdnQWNmKGV1Y3BpX2RmJGNwX2luZGV4LCBsYWcubWF4ID0gNzAwKQpgYGAKCkxJTkVBUiBSRUdSRVNTSU9OCmBgYHtyfQp0cmFpbl9kYXRhIDwtIGV1Y3BpX2RmWzE6KG5yb3coZXVjcGlfZGYpIC0gMyksIF0KdGVzdF9kYXRhIDwtIGV1Y3BpX2RmWyhucm93KGV1Y3BpX2RmKSAtIDIpOm5yb3coZXVjcGlfZGYpLCBdCgptb2RlbCA8LSBsbShjcF9pbmRleCB+IGRhdGUsIGRhdGEgPSB0cmFpbl9kYXRhKQoKc3VtbWFyeShtb2RlbCkKYGBgCgpgYGB7cn0KcHJlZGljdGlvbnMgPC0gcHJlZGljdChtb2RlbCwgbmV3ZGF0YSA9IHRlc3RfZGF0YSkKcmVzaWR1YWxzIDwtIHJlc2lkdWFscyhtb2RlbCkKcmVzaWR1YWxzX3Rlc3QgPC0gcmVzaWR1YWxzKG1vZGVsKVsxOm5yb3codGVzdF9kYXRhKV0KYGBgCgoKYGBge3J9CnJtc2VfdmFsdWUgPC0gcm1zZShwcmVkaWN0aW9ucywgdGVzdF9kYXRhJGNwX2luZGV4KQptYWVfdmFsdWUgPC0gbWFlKHByZWRpY3Rpb25zLCB0ZXN0X2RhdGEkY3BfaW5kZXgpCgpjYXQoIlJNU0U6Iiwgcm1zZV92YWx1ZSwgIlxuIikKY2F0KCJNQUU6IiwgbWFlX3ZhbHVlLCAiXG4iKQpgYGAKCgpgYGB7cn0KcGxvdCh0ZXN0X2RhdGEkZGF0ZSwgdGVzdF9kYXRhJGNwX2luZGV4LCBjb2wgPSAiYmx1ZSIsIHR5cGUgPSAibCIsIHhsYWIgPSAiRGF0ZSIsIHlsYWIgPSAiRVUgQ29uc3VtZXIgUHJpY2UgSU5kZXgiKQpsaW5lcyh0ZXN0X2RhdGEkZGF0ZSwgcHJlZGljdGlvbnMsIGNvbCA9ICJyZWQiKQpsZWdlbmQoInRvcGxlZnQiLCBsZWdlbmQgPSBjKCJBY3R1YWwiLCAiUHJlZGljdGVkIiksIGNvbCA9IGMoImJsdWUiLCAicmVkIiksIGx0eSA9IDEpCmBgYApgYGB7cn0KIyBSZXNpZHVhbHMgcGxvdApwbG90KHRlc3RfZGF0YSRkYXRlLCByZXNpZHVhbHNfdGVzdCwgY29sID0gImdyZWVuIiwgdHlwZSA9ICJsIiwgeGxhYiA9ICJEYXRlIiwgeWxhYiA9ICJSZXNpZHVhbHMgdGVzdCIpCgpwbG90KHRyYWluX2RhdGEkZGF0ZSwgcmVzaWR1YWxzLCBjb2wgPSAiZ3JlZW4iLCB0eXBlID0gImwiLCB4bGFiID0gIkRhdGUiLCB5bGFiID0gIlJlc2lkdWFscyB0cmFpbiIpCmBgYApgYGB7cn0KcmVzdWx0X3RhYmxlIDwtIGRhdGEuZnJhbWUoCiAgRGF0ZSA9IHRlc3RfZGF0YSRkYXRlLAogIEFjdHVhbCA9IHRlc3RfZGF0YSRjcF9pbmRleCwKICBQcmVkaWN0ZWQgPSBwcmVkaWN0aW9ucwopCgpwcmludChyZXN1bHRfdGFibGUpCmBgYApgYGB7cn0KZHd0ZXN0KG1vZGVsKQpgYGAKCkFSSU1BIChDUCBJbmRleCkKYGBge3J9CnRyYWluX2RhdGEgPC0gZXVjcGlfZGZbMToobnJvdyhldWNwaV9kZikgLSAzKSwgXQp0ZXN0X2RhdGEgPC0gZXVjcGlfZGZbKG5yb3coZXVjcGlfZGYpIC0gMik6bnJvdyhldWNwaV9kZiksIF0KCmFyaW1hX21vZGVsIDwtIGF1dG8uYXJpbWEodHJhaW5fZGF0YSRjcF9pbmRleCkKCnN1bW1hcnkoYXJpbWFfbW9kZWwpCmBgYApgYGB7cn0KIyBGb3JlY2FzdGluZyBmb3IgdGhlIG5leHQgMyBtb250aHMKYXJpbWFfZm9yZWNhc3QgPC0gZm9yZWNhc3QoYXJpbWFfbW9kZWwsIGggPSAzKQphcmltYV9wcmVkX3ZhbHVlcyA8LSBhcmltYV9mb3JlY2FzdCRtZWFuCnByaW50KGFyaW1hX2ZvcmVjYXN0KQpgYGAKYGBge3J9CiMgUGxvdHRpbmcgQVJJTUEgRm9yZWNhc3QKcGxvdChhcmltYV9mb3JlY2FzdCwgeGxhYiA9ICJEYXRlIiwgeWxhYiA9ICJDUCBJbmRleCBGb3JlY2FzdCIpCmBgYAoKCmBgYHtyfQojIEFSSU1BIHJlc2lkdWFscwphcmltYV9yZXNpZHVhbHMgPC0gcmVzaWR1YWxzKGFyaW1hX21vZGVsKQpwbG90KHRyYWluX2RhdGEkZGF0ZSwgYXJpbWFfcmVzaWR1YWxzLCBjb2wgPSAiZ3JlZW4iLCB0eXBlID0gImwiLCB4bGFiID0gIkRhdGUiLCB5bGFiID0gIkFSSU1BIFJlc2lkdWFscyIpCmBgYAoKCmBgYHtyfQpmb3JlY2FzdGVkX3ZhbHVlcyA8LSBhcmltYV9mb3JlY2FzdCRtZWFuCmFjdHVhbF92YWx1ZXMgPC0gYXMubnVtZXJpYyh0ZXN0X2RhdGEkY3BfaW5kZXgpCmFyaW1hX3Jlc2lkdWFsc190ZXN0IDwtIGFjdHVhbF92YWx1ZXMgLSBmb3JlY2FzdGVkX3ZhbHVlcwpgYGAKCgpgYGB7cn0KI0FSSU1BIGZvcmVjYXN0ZWQgdmFsdWVzIGFuZCByZXNpZHVhbHMKYXJpbWFfdGFibGUgPC0gZGF0YS5mcmFtZSgKICBEYXRlID0gaW5kZXgodGVzdF9kYXRhKSwKICBBY3R1YWxfVmFsdWVzID0gYWN0dWFsX3ZhbHVlcywKICBGb3JlY2FzdGVkX0NQX0luZGV4ID0gZm9yZWNhc3RlZF92YWx1ZXMsCiAgUmVzaWR1YWxzID0gYXJpbWFfcmVzaWR1YWxzX3Rlc3QKKQoKIyBQcmludCB0aGUgQVJJTUEgZm9yZWNhc3QgYW5kIHJlc2lkdWFscyB0YWJsZQpwcmludChhcmltYV90YWJsZSkKCmBgYApgYGB7cn0KIyBQbG90dGluZyBBUklNQSBwcmVkaWN0ZWQgdmFsdWVzIGFuZCByZXNpZHVhbHMgZm9yIHRoZSB0ZXN0IGRhdGEKcGxvdCh0ZXN0X2RhdGEkZGF0ZSwgYXJpbWFfcHJlZF92YWx1ZXMsIGNvbCA9ICJibHVlIiwgdHlwZSA9ICJsIiwgeGxhYiA9ICJEYXRlIiwgeWxhYiA9ICJBUklNQSBQcmVkaWN0ZWQgVmFsdWVzIikKbGluZXModGVzdF9kYXRhJGRhdGUsIGFyaW1hX3Jlc2lkdWFsc190ZXN0LCBjb2wgPSAiZ3JlZW4iKQpsZWdlbmQoInRvcHJpZ2h0IiwgbGVnZW5kID0gYygiUHJlZGljdGVkIFZhbHVlcyIsICJSZXNpZHVhbHMiKSwgY29sID0gYygiYmx1ZSIsICJncmVlbiIpLCBsdHkgPSAxKQpgYGAKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKVVMgUkFURQpgYGB7cn0KcmF0ZSRQZXJpb2QuVW5pdC4gPC0gYXMuRGF0ZShyYXRlJFBlcmlvZC5Vbml0LikKcmF0ZV9kZiA8LSBkYXRhLmZyYW1lKERhdGUgPSByYXRlJFBlcmlvZC5Vbml0LiwgVVNfcmF0ZSA9IHJhdGUkWC5VUy5kb2xsYXIuLikKYGBgCgpgYGB7cn0KcmF0ZV9kZiA8LSByYXRlX2RmICU+JSAKICBtdXRhdGUoRGF0ZSA9IGZvcm1hdChEYXRlLCAiJVktJW0iKSkKaGVhZChyYXRlX2RmKQpgYGAKYGBge3J9CmZpbHRlcmVkX2RhdGEgPC0gcmF0ZV9kZiAlPiUKICBncm91cF9ieShEYXRlKSAlPiUKICBzdW1tYXJpemUobWVhbl9yYXRlID0gbWVhbihhcy5udW1lcmljKFVTX3JhdGUpKSkKYGBgCmBgYHtyfQpmaWx0ZXJlZF9kYXRhJERhdGUgPC0gcGFzdGUwKGZpbHRlcmVkX2RhdGEkRGF0ZSwgJy0wMScpCmZpbHRlcmVkX2RhdGEkRGF0ZSA8LSBhcy5EYXRlKGZpbHRlcmVkX2RhdGEkRGF0ZSkKaGVhZChmaWx0ZXJlZF9kYXRhKQpgYGAKCmBgYHtyfQpzdW1tYXJ5KGZpbHRlcmVkX2RhdGEkbWVhbl9yYXRlKQpgYGAKCmBgYHtyfQpnZ3Bsb3QoZmlsdGVyZWRfZGF0YSwgYWVzKHggPSBEYXRlLCB5ID0gbWVhbl9yYXRlKSkgKwogIGdlb21fbGluZSgpICsKICBsYWJzKAogICAgdGl0bGUgPSAiVVMgUmF0ZSBPdmVyIFRpbWUiLAogICAgeCA9ICJUaW1lIiwgCiAgICB5ID0gIlVTIFJhdGUiCiAgICApCmBgYAoKYGBge3J9CmdnIDwtIGdncGxvdChmaWx0ZXJlZF9kYXRhLCBhZXMoeCA9IERhdGUsIHkgPSBtZWFuX3JhdGUpKSsKICBnZW9tX2xpbmUoYWVzKGdyb3VwID0gMSksIGNvbG9yID0gImJsdWUiLCBsaW5ld2lkdGggPSAwLjUpICsKICBnZW9tX3BvaW50KCkgKwogIGxhYnMoCiAgICB0aXRsZSA9ICJVUyBSYXRlIiwKICAgIHggPSAiVGltZSIsCiAgICB5ID0gIlJhdGUiCiAgKSArCiAgc2NhbGVfeF9kYXRlKGRhdGVfbGFiZWxzID0gIiVZLSVtIiwgZGF0ZV9icmVha3MgPSAiMSB5ZWFyIikgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHZqdXN0ID0gMC41LCBoanVzdD0xKSkKCnBsb3RseV9nZyA8LSBnZ3Bsb3RseShnZykKCnBsb3RseV9nZwpgYGAKYGBge3J9CnN0cihldWlyX2RmKQpzdHIoZmlsdGVyZWRfZGF0YSkKYGBgCgpgYGB7cn0KbWVyZ2VkX2RmIDwtIG1lcmdlKG1lcmdlKGV1aXJfZGYsIGV1Y3BpX2RmLCBieSA9ICJkYXRlIiksIGZpbHRlcmVkX2RhdGEsIGJ5LnggPSAiZGF0ZSIsIGJ5LnkgPSAiRGF0ZSIpCm5hbWVzKG1lcmdlZF9kZikgPC0gYygiZGF0ZSIsICJldV9pciIsICJldV9jcGkiLCAidXNfcmF0ZSIpCmhlYWQobWVyZ2VkX2RmKQp0YWlsKG1lcmdlZF9kZikKYGBgCgpBUklNQVggKFVTIHJhdGUgKyBFVSBpbnRlcmVzdCByYXRlKQpgYGB7cn0KbWVyZ2VkX2RmJGRhdGUgPC0gYXMuRGF0ZShtZXJnZWRfZGYkZGF0ZSkKdXNfdHMgPC0gdHMobWVyZ2VkX2RmJHVzX3JhdGUsIGZyZXF1ZW5jeSA9IDEyLCBzdGFydCA9IGMoMTk5OSwgMSkpCmBgYAoKYGBge3J9CiMgU3BsaXQgdGhlIGRhdGEgaW50byB0cmFpbiBhbmQgdGVzdCBzZXRzCnRyYWluX2RhdGEgPC0gd2luZG93KHVzX3RzLCBzdGFydCA9IGMoMTk5OSwgMSksIGVuZCA9IGMoMjAyMiwgMTApKQp0ZXN0X2RhdGEgPC0gd2luZG93KHVzX3RzLCBzdGFydCA9IGMoMjAyMiwgMTEpKQoKZXh0ZXJuYWxfcmVncmVzc29yIDwtIG1lcmdlZF9kZiRldV9pclttZXJnZWRfZGYkZGF0ZSA+PSBhcy5EYXRlKCIxOTk5LTAxLTAxIikgJiBtZXJnZWRfZGYkZGF0ZSA8PSBhcy5EYXRlKCIyMDIyLTEwLTAxIildCgpgYGAKCmBgYHtyfQpmaXQgPC0gQXJpbWEodHJhaW5fZGF0YSwgb3JkZXIgPSBjKDEsIDAsIDEpLCB4cmVnID0gZXh0ZXJuYWxfcmVncmVzc29yKQoKZm9yZWNhc3RfdmFsdWVzIDwtIGZvcmVjYXN0KGZpdCwgeHJlZyA9IG1lcmdlZF9kZiRldV9pclttZXJnZWRfZGYkZGF0ZSA+PSBhcy5EYXRlKCIyMDIyLTExLTAxIildLCBoID0gMykKCmBgYAoKCmBgYHtyfQpwcmVkaWN0ZWRfdmFsdWVzIDwtIGZvcmVjYXN0X3ZhbHVlcyRtZWFuCmFjdHVhbF92YWx1ZXMgPC0gd2luZG93KHVzX3RzLCBzdGFydCA9IGMoMjAyMiwgMTEpKQpgYGAKCgpgYGB7cn0KcmVzdWx0X3RhYmxlIDwtIHRpYmJsZSgKICBEYXRlID0gdGltZShhY3R1YWxfdmFsdWVzKSwKICBBY3R1YWxfVmFsdWVzID0gYWN0dWFsX3ZhbHVlcywKICBQcmVkaWN0ZWRfVmFsdWVzID0gcHJlZGljdGVkX3ZhbHVlcwopCmBgYAoKYGBge3J9CnBsb3QoZm9yZWNhc3RfdmFsdWVzLCBtYWluID0gIkFSSU1BWCBGb3JlY2FzdCBmb3IgVVMgUmF0ZSIpCmxpbmVzKGFjdHVhbF92YWx1ZXMsIGNvbCA9ICJibHVlIikKbGVnZW5kKCJ0b3BsZWZ0IiwgbGVnZW5kID0gYygiQWN0dWFsIiwgIkZvcmVjYXN0IiksIGNvbCA9IGMoImJsdWUiLCAiYmxhY2siKSwgbHR5ID0gMSkKCmBgYApgYGB7cn0KcHJpbnQocmVzdWx0X3RhYmxlKQpgYGAKQVJJTUFYIChVUyByYXRlICsgRVUgQ29uc3VtZXIgUHJpY2UgSW5kZXgpCmBgYHtyfQojIFNwbGl0IHRoZSBkYXRhIGludG8gdHJhaW4gYW5kIHRlc3Qgc2V0cwp0cmFpbl9kYXRhIDwtIHdpbmRvdyh1c190cywgc3RhcnQgPSBjKDE5OTksIDEpLCBlbmQgPSBjKDIwMjIsIDEwKSkKdGVzdF9kYXRhIDwtIHdpbmRvdyh1c190cywgc3RhcnQgPSBjKDIwMjIsIDExKSkKCmV4dGVybmFsX3JlZ3Jlc3NvciA8LSBtZXJnZWRfZGYkZXVfY3BpW21lcmdlZF9kZiRkYXRlID49IGFzLkRhdGUoIjE5OTktMDEtMDEiKSAmIG1lcmdlZF9kZiRkYXRlIDw9IGFzLkRhdGUoIjIwMjItMTAtMDEiKV0KYGBgCgoKYGBge3J9CmZpdCA8LSBBcmltYSh0cmFpbl9kYXRhLCBvcmRlciA9IGMoMSwgMCwgMSksIHhyZWcgPSBleHRlcm5hbF9yZWdyZXNzb3IpCgpmb3JlY2FzdF92YWx1ZXMgPC0gZm9yZWNhc3QoZml0LCB4cmVnID0gbWVyZ2VkX2RmJGV1X2NwaVttZXJnZWRfZGYkZGF0ZSA+PSBhcy5EYXRlKCIyMDIyLTExLTAxIildLCBoID0gMykKYGBgCgoKYGBge3J9CnByZWRpY3RlZF92YWx1ZXMgPC0gZm9yZWNhc3RfdmFsdWVzJG1lYW4KYWN0dWFsX3ZhbHVlcyA8LSB3aW5kb3codXNfdHMsIHN0YXJ0ID0gYygyMDIyLCAxMSkpCmBgYAoKCmBgYHtyfQpyZXN1bHRfdGFibGUgPC0gdGliYmxlKAogIERhdGUgPSB0aW1lKGFjdHVhbF92YWx1ZXMpLAogIEFjdHVhbF9WYWx1ZXMgPSBhY3R1YWxfdmFsdWVzLAogIFByZWRpY3RlZF9WYWx1ZXMgPSBwcmVkaWN0ZWRfdmFsdWVzCikKYGBgCgoKYGBge3J9CnBsb3QoZm9yZWNhc3RfdmFsdWVzLCBtYWluID0gIkFSSU1BWCBGb3JlY2FzdCBmb3IgVVMgUmF0ZSIpCmxpbmVzKGFjdHVhbF92YWx1ZXMsIGNvbCA9ICJibHVlIikKbGVnZW5kKCJ0b3BsZWZ0IiwgbGVnZW5kID0gYygiQWN0dWFsIiwgIkZvcmVjYXN0IiksIGNvbCA9IGMoImJsdWUiLCAiYmxhY2siKSwgbHR5ID0gMSkKCmBgYAoKCmBgYHtyfQpwcmludChyZXN1bHRfdGFibGUpCmBgYAoKCmBgYHtyfQpgYGAKCg==